package dao

import (
	"context"
	"errors"
	"fmt"

	"bilinadmin/pkg/util"

	"bilinadmin/pkg/db/pgex"

	"bilinadmin/internal/model"
)

// RoleDao role dao
type RoleDao interface {
	// CreateEmpRole 创建角色
	CreateEmpRole(ctx context.Context, role *model.EmpRole) (int64, error)
	// UpdateEmpRole 更新角色
	UpdateEmpRole(ctx context.Context, role *model.EmpRole) error
	// CreateRolePermissions 创建角色权限
	CreateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error
	// UpdateRolePermissions 更新角色权限
	UpdateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error
	// CreateOrUpdateRolePermissions 创建或更新角色权限
	CreateOrUpdateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error
	// GetRoleById 获取角色聚合 by id
	GetRoleById(ctx context.Context, roleId int64) (*model.RoleAgg, error)
	// GetEmpRoleByIdAndRef 获取角色信息
	GetEmpRoleByIdAndRef(ctx context.Context, roleId, refId int64, refType int8) (*model.EmpRole, error)
	// GetRolePermission 获取角色权限
	GetRolePermission(ctx context.Context, roleId, perId int64) (*model.RolePermission, error)
	// GetEmpRoleById 获取角色信息 by id
	GetEmpRoleById(ctx context.Context, roleId int64) (*model.EmpRole, error)
	// GetEmpRolesByIds 获取角色信息 by ids
	GetEmpRolesByIds(ctx context.Context, roleIds []int64) ([]*model.EmpRole, error)
	// GetRolesAggByRef 获取角色聚合信息
	GetRolesAggByRef(ctx context.Context, refId int64, refType int8) ([]*model.RoleAgg, error)
	// GetEmpRoleByName 根据角色名获取
	GetEmpRoleByName(ctx context.Context, refId int64, refType int8, name string) (*model.EmpRole, error)
}

var roleDao RoleDao

type roleImpl struct {
	*DaoBase
	name string
}

// InitRoleDao 初始化 dao
func InitRoleDao() {
	roleDao = &roleImpl{
		DaoBase: daoBase,
		name:    "role_dao",
	}
}

// GetRoleDao 获取 dao
func GetRoleDao() RoleDao {
	return roleDao
}

// CreateEmpRole 创建角色
func (dao *roleImpl) CreateEmpRole(ctx context.Context, role *model.EmpRole) (int64, error) {
	if role == nil {
		return 0, nil
	}
	param := []interface{}{
		role.BrandId,
		role.RefId,
		role.RefType,
		role.Name,
		role.IsAdmin,
		role.ModifyEmpId,
		role.CreatedAt,
		role.UpdatedAt,
	}
	query := "INSERT INTO emp_role (brand_id, ref_id, ref_type, name, is_admin, modify_emp_id, created_at, updated_at) " +
		"VALUES (?,?,?,?,?,?,?,?)"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, _ := aff.GetLastId()
	return lastId, nil
}

// UpdateEmpRole 更新角色
func (dao *roleImpl) UpdateEmpRole(ctx context.Context, role *model.EmpRole) error {
	if role == nil {
		return nil
	}
	param := []interface{}{
		role.Name,
		role.ModifyEmpId,
		role.UpdatedAt,
		role.Id,
	}
	query := "UPDATE emp_role SET name=?, modify_emp_id=?, updated_at=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, param...)
	return err
}

// CreateRolePermissions 创建角色权限
func (dao *roleImpl) CreateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error {
	if len(perms) == 0 {
		return nil
	}
	param := make([][]interface{}, len(perms))
	for idx, p := range perms {
		param[idx] = []interface{}{
			p.RoleId,
			p.PerId,
			p.SubPerIds,
			p.ModifyEmpId,
			p.CreatedAt,
			p.UpdatedAt,
		}
	}
	query := "INSERT INTO role_permission (role_id, per_id, sub_per_ids, modify_emp_id, created_at, updated_at) " +
		"VALUES (?,?,?,?,?,?)"
	_, err := dao.db.InsertMany(ctx, query, param)
	return err
}

// UpdateRolePermissions 更新角色权限
func (dao *roleImpl) UpdateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error {
	if len(perms) == 0 {
		return nil
	}
	param := make([][]interface{}, len(perms))
	for idx, p := range perms {
		param[idx] = []interface{}{
			p.SubPerIds,
			p.ModifyEmpId,
			p.UpdatedAt,
			p.RoleId,
			p.PerId,
		}
	}
	query := "UPDATE role_permission SET sub_per_ids=?, modify_emp_id=?, updated_at=? WHERE role_id=? AND per_id=? AND deleted_at=0"
	_, err := dao.db.UpdateMany(ctx, query, param)
	return err
}

func (dao *roleImpl) DeleteRolePermissions(ctx context.Context, ids []int64) error {
	if len(ids) == 0 {
		return nil
	}
	now := util.GetNow()
	query := "UPDATE role_permission SET deleted_at=?, updated_at=? WHERE id in ?"
	_, err := dao.db.Delete(ctx, query, util.TimeToTimestamp(now), now, ids)
	return err
}

// CreateOrUpdateRolePermissions 创建或更新角色权限
func (dao *roleImpl) CreateOrUpdateRolePermissions(ctx context.Context, perms ...*model.RolePermission) error {
	if err := dao.needTrans(ctx); err != nil {
		return err
	}
	// 锁定已有的
	roleIds := make([]int64, len(perms))
	perMap := map[string]*model.RolePermission{}
	for idx, p := range perms {
		roleIds[idx] = p.RoleId
		perMap[fmt.Sprintf("%d:%d", p.RoleId, p.PerId)] = p
	}
	// 锁定现存的
	query := "SELECT id, role_id, per_id FROM role_permission WHERE role_id IN ? AND deleted_at=0 FOR UPDATE"
	var rolePerms []*model.RolePermission
	err := dao.db.Query(ctx, &rolePerms, query, roleIds)
	if err != nil {
		return err
	}
	// 分离出需要更新的
	var updateItems []*model.RolePermission
	var newItems []*model.RolePermission
	exitKeys := make(map[string]int64, len(rolePerms))
	for _, p := range rolePerms {
		exitKeys[fmt.Sprintf("%d:%d", p.RoleId, p.PerId)] = p.Id
	}
	for key, p := range perMap {
		_, found := exitKeys[key]
		if found {
			updateItems = append(updateItems, p)
			delete(exitKeys, key) // 剩下的是需要删除的
		} else {
			newItems = append(newItems, p)
		}
	}
	// 分离出需要删除的
	var delItems []int64
	for _, id := range exitKeys {
		delItems = append(delItems, id)
	}
	// 新建
	err = dao.CreateRolePermissions(ctx, newItems...)
	if err != nil {
		return err
	}
	// 更新
	err = dao.UpdateRolePermissions(ctx, updateItems...)
	if err != nil {
		return err
	}
	// 删除
	return dao.DeleteRolePermissions(ctx, delItems)
}

// GetRoleById 获取角色聚合 by id
func (dao *roleImpl) GetRoleById(ctx context.Context, roleId int64) (*model.RoleAgg, error) {
	role := &model.EmpRole{}
	query := "SELECT * FROM emp_role WHERE id=? AND deleted_at=0"
	err := dao.db.Get(ctx, role, query, roleId)
	if err != nil {
		return nil, err
	}
	var rolePerms []*model.RolePermission
	query = "SELECT * FROM role_permission WHERE role_id=? AND deleted_at=0"
	err = dao.db.Query(ctx, &rolePerms, query, role.Id)
	if err != nil {
		return nil, err
	}
	return model.NewRole(role, rolePerms), nil
}

// GetEmpRoleByIdAndRef 获取角色信息
func (dao *roleImpl) GetEmpRoleByIdAndRef(ctx context.Context, roleId, refId int64, refType int8) (*model.EmpRole, error) {
	role := &model.EmpRole{}
	query := "SELECT * FROM emp_role WHERE id=? AND ref_id=? AND ref_type=? AND deleted_at=0"
	err := dao.db.Get(ctx, role, query, roleId, refId, refType)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return role, err
}

// GetEmpRoleById 获取角色信息
func (dao *roleImpl) GetEmpRoleById(ctx context.Context, roleId int64) (*model.EmpRole, error) {
	role := &model.EmpRole{}
	query := "SELECT * FROM emp_role WHERE id=? AND deleted_at=0"
	err := dao.db.Get(ctx, role, query, roleId)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return role, err
}

// GetEmpRoleByName 根据角色名获取
func (dao *roleImpl) GetEmpRoleByName(ctx context.Context, refId int64, refType int8, name string) (*model.EmpRole, error) {
	role := &model.EmpRole{}
	query := "SELECT * FROM emp_role WHERE ref_id=? AND ref_type=? AND name=? AND deleted_at=0"
	err := dao.db.Get(ctx, role, query, refId, refType, name)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return role, err
}

// GetEMpRolesByIds 获取角色信息 by ids
func (dao *roleImpl) GetEmpRolesByIds(ctx context.Context, roleIds []int64) ([]*model.EmpRole, error) {
	if len(roleIds) == 0 {
		return nil, nil
	}
	query := "SELECT id, brand_id, ref_id, ref_type, name, is_admin FROM emp_role WHERE id IN ? AND deleted_at=0"
	var roles []*model.EmpRole
	err := dao.db.Query(ctx, &roles, query, roleIds)
	return roles, err
}

// GetRolePermission 获取角色权限
func (dao *roleImpl) GetRolePermission(ctx context.Context, roleId, perId int64) (*model.RolePermission, error) {
	rolePerm := &model.RolePermission{}
	query := "SELECT * FROM role_permission WHERE role_id=? AND per_id=? AND deleted_at=0"
	err := dao.db.Get(ctx, rolePerm, query, roleId, perId)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return rolePerm, err
}

// GetRolesAggByRef 获取角色信息
func (dao *roleImpl) GetRolesAggByRef(ctx context.Context, refId int64, refType int8) ([]*model.RoleAgg, error) {
	// 获取角色
	var empRoles []*model.EmpRole
	query := "SELECT id, brand_id, ref_id, ref_type, name, is_admin FROM emp_role WHERE ref_id=? AND ref_type=? AND deleted_at=0"
	err := dao.db.Query(ctx, &empRoles, query, refId, refType)
	if err != nil {
		return nil, err
	}
	roleIds := make([]int64, len(empRoles))
	for idx, er := range empRoles {
		roleIds[idx] = er.Id
	}
	// 获取角色权限
	var rolePerms []*model.RolePermission
	query = "SELECT role_id, per_id, sub_per_ids FROM role_permission WHERE role_id IN ? AND deleted_at=0"
	err = dao.db.Query(ctx, &rolePerms, query, roleIds)
	if err != nil {
		return nil, err
	}
	permsMap := map[int64][]*model.RolePermission{}
	for _, p := range rolePerms {
		perms, ok := permsMap[p.RoleId]
		if !ok {
			perms = []*model.RolePermission{}
		}
		perms = append(perms, p)
		permsMap[p.RoleId] = perms
	}
	// 角色聚合
	ret := make([]*model.RoleAgg, len(empRoles))
	for idx, er := range empRoles {
		perms, _ := permsMap[er.Id]
		ret[idx] = model.NewRole(er, perms)
	}
	return ret, nil
}
